package net.cyue.easyconfiguration.node.util;

import net.cyue.easyconfiguration.node.NodeType;
import net.cyue.easyconfiguration.node.api.INodeTypeConverter;
import net.cyue.easyconfiguration.node.data.NodeMetaData;
import net.cyue.easyconfiguration.node.definition.*;
import net.cyue.util.ReflectUtil;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

public class NodeDefinitionFactor {

    private static final Logger LOGGER = Logger.getLogger(NodeDefinitionFactor.class.getName());

    private NodeDefinitionFactor() {
    }

    /**
     * 创建数组节点定义
     * @param parent 父节点
     * @param elementClz 元素 JavaType
     * @param name 名称
     * @param typeConverter 节点类型转换器
     * @param elementTypeConverter 元素类型转换器
     * @param paths 节点路径
     * @return 数组节点定义
     */
    public static ArrayNodeDefinition createArrayNodeDefinition(
        AbstractNodeDefinition parent,
        Class<?> elementClz,
        String name,
        INodeTypeConverter typeConverter,
        INodeTypeConverter elementTypeConverter,
        boolean forceConvert,
        boolean elementForceConvert,
        String... paths
    ) {
        ArrayNodeDefinition definition = new ArrayNodeDefinition(
            parent,
            typeConverter,
            name,
            paths
        );
        definition.setElementJavaType(elementClz);
        definition.setForceConvert(forceConvert);

        // 初始化 element definition
        // 基本类型
        if (elementClz.isPrimitive()) {
            definition.setElementNodeType(NodeType.NUMBER);
        }
        // 包装类型
        else if (elementClz.isAssignableFrom(String.class)) {
            definition.setElementNodeType(NodeType.STRING);
        }
        else if (elementClz.isAssignableFrom(Number.class)) {
            definition.setElementNodeType(NodeType.NUMBER);
        }
        else if (elementClz.isAssignableFrom(Boolean.class)) {
            definition.setElementNodeType(NodeType.BOOLEAN);
        }
        // 数组或列表或集合
        else if (elementClz.isArray()) {
            // 数组元素系数组，则递归
            /// 元素的元素的 JavaType
            Class<?> elementElementClz = elementClz.getComponentType();
            definition.setElementDefinition(
                createArrayElementNodeDefinition(
                    definition,
                    elementElementClz,
                    definition.getName() + "-element",
                    NodeType.ARRAY,
                    elementTypeConverter,
                    elementForceConvert
                )
            );
        }
        else if (elementClz.isAssignableFrom(List.class)) {
            // TODO
            LOGGER.warning("数组节点的元素 JavaType 是列表");
        }
        else if (elementClz.isAssignableFrom(Set.class)) {
            // TODO
            LOGGER.warning("数组节点的元素 JavaType 是集合");
        }
        // map 或 any（table）
        else {
            definition.setElementDefinition(
                createTableNodeDefinition(
                    definition,
                    elementClz,
                    definition.getName() + "-element",
                    elementTypeConverter,
                    elementForceConvert
                )
            );
        }

        return definition;
    }

    /**
     * 创建表节点定义
     * @param parent 父节点
     * @param clz 节点 JavaType
     * @param name 名称
     * @param typeConverter 节点类型转换器
     * @param paths 节点路径
     * @return 表节点定义
     */
    public static TableNodeDefinition createTableNodeDefinition(
        AbstractNodeDefinition parent,
        Class<?> clz,
        String name,
        INodeTypeConverter typeConverter,
        boolean forceConvert,
        String... paths
    ) {
        TableNodeDefinition definition = new TableNodeDefinition(
            parent,
            typeConverter,
            name,
            paths
        );
        definition.setJavaType(clz);
        definition.setForceConvert(forceConvert);

        // 忽略 object 与 map
        if (
            clz.equals(Object.class) ||
            clz.isAssignableFrom(Map.class)
        ) {
            return definition;
        }
        // 获取字段定义
        for (Field f : ReflectUtil.getAllFields(clz)) {
            NodeMetaData fieldMetaData = NodeMetaData.fromField(f);
            AbstractNodeDefinition nodeDefinition = NodeDefinitionFactor.create(fieldMetaData);
            nodeDefinition.setForceConvert(fieldMetaData.isForceConvert());
            if (nodeDefinition.isIgnore()) {
                continue;
            }
            definition.addFieldDefinition(
                f.getName(),
                nodeDefinition
            );
        }

        return  definition;
    }

    /**
     * 创建数组元素节点定义
     * @param parent 父节点（数组节点）
     * @param clz 元素 JavaType
     * @param name 名称
     * @param nodeType 节点类型
     * @param typeConverter 节点类型转换器
     * @return 数组元素节点定义
     */
    public static AbstractNodeDefinition createArrayElementNodeDefinition(
        ArrayNodeDefinition parent,
        Class<?> clz,
        String name,
        NodeType nodeType,
        INodeTypeConverter typeConverter,
        boolean forceConvert
    ) {
        AbstractNodeDefinition nodeDefinition;
        if (
            nodeType == NodeType.STRING ||
            nodeType == NodeType.NUMBER ||
            nodeType == NodeType.BOOLEAN
        ) {
            return NodeDefinitionFactor.create(parent, nodeType, name);
        }
        // 元素是数组
        else if (nodeType == NodeType.ARRAY) {
            // TODO clz 不是数组
            if (clz.isArray()) {
                nodeDefinition = createArrayNodeDefinition(
                    parent,
                    clz.getComponentType(),
                    name + "-element",
                    typeConverter,
                    null,
                    forceConvert,
                    forceConvert
                );
            } else {
                LOGGER.warning("数组节点的 JavaType 不是数组，其元素 JavaType 使用 Object 代替");
                nodeDefinition = createArrayNodeDefinition(
                    parent,
                    Object.class,
                    name + "-element",
                    typeConverter,
                    null,
                    forceConvert,
                    forceConvert
                );
            }
        }
        // table or any
        else {
            nodeDefinition = createTableNodeDefinition(
                parent,
                clz,
                name,
                typeConverter,
                forceConvert
            );
        }

        nodeDefinition.setJavaType(clz);
        return nodeDefinition;
    }

    public static AbstractNodeDefinition create(
        AbstractNodeDefinition parent,
        NodeType type,
        String name,
        String... paths
    ) {
        return create(
            parent,
            type,
            null,
            name,
            paths
        );
    }
    public static AbstractNodeDefinition create(
        AbstractNodeDefinition parent,
        NodeType type,
        INodeTypeConverter typeConverter,
        String name,
        String... paths
    ) {
        String[] normalizedPaths = NodePathUtil.normalizePaths(paths);
        switch (type) {
            case STRING:
                return new StringNodeDefinition(parent, typeConverter, name, normalizedPaths);
            case NUMBER:
                return new NumberNodeDefinition(parent, typeConverter, name, normalizedPaths);
            case BOOLEAN:
                return new BooleanNodeDefinition(parent, typeConverter, name, normalizedPaths);
            case ARRAY:
                throw new IllegalArgumentException(
                    "Unsupported node type：" + type +
                    "，please use createArrayNodeDefinition"
                );
            case TABLE:
                throw new IllegalArgumentException(
                    "Unsupported node type：" + type +
                    "，please use createTableNodeDefinition"
                );
            default:
                return new AnyNodeDefinition(parent, type, typeConverter, name, normalizedPaths);
        }
    }
    

    public static AbstractNodeDefinition create(NodeMetaData metaData) {
        if (metaData.isIgnore()) {
            return new IgnoreNodeDefinition(
                metaData.getType(),
                metaData.getName(),
                metaData.getPaths()
            );
        }

        AbstractNodeDefinition nodeDefinition;
        // 特殊处理数组类型，递归初始化 element definition 与 table definition
        if (metaData.getType() == NodeType.ARRAY) {
            nodeDefinition = createArrayNodeDefinition(
                null,
                metaData.getElementJavaType(),
                metaData.getName(),
                metaData.getTypeConverter(),
                metaData.getElementTypeConverter(),
                metaData.isForceConvert(),
                metaData.isElementForceConvert(),
                metaData.getPaths()
            );
        }
        // 特殊处理表类型（忽略Map），递归初始化 element definition 与 table definition
        else if (
            metaData.getType() == NodeType.TABLE &&
            !metaData.getJavaType().isAssignableFrom(Map.class)
        ) {
            nodeDefinition = createTableNodeDefinition(
                null,
                metaData.getJavaType(),
                metaData.getName(),
                metaData.getTypeConverter(),
                metaData.isForceConvert(),
                metaData.getPaths()
            );
        }
        // 普通节点
        else {
            nodeDefinition = create(
                null,
                metaData.getType(),
                metaData.getTypeConverter(),
                metaData.getName(),
                metaData.getPaths()
            );
        }

        nodeDefinition.setMultiNode(metaData.isMultiNode());
        nodeDefinition.setJavaType(metaData.getJavaType());
        nodeDefinition.setForceConvert(metaData.isForceConvert());
        return nodeDefinition;
    }
}
